package com.test.springsecurity.configuration;

import com.test.springsecurity.filter.JwtOnce;
import com.test.springsecurity.handler.MyExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;

/**
 * SpringSecurity配置类 ：用于配置系统的用户信息、登录信息、登出信息、跨域配置。csrf配置等
 */
@Configuration
public class MySpringSecurityConfiguration extends WebSecurityConfigurerAdapter {

    //注入对象 用户信息来源
    @Resource
    private UserDetailsService userDetailsService;
    //用来配置用户信息有哪些 从哪里来 常见有二种配置方式

    /**
     * 1.用户信息来自于于内存 （在内存 临时用户信息） 仅限于学习期间使用
     * 2、同数据库获取用户信息
     * @param auth the {@link AuthenticationManagerBuilder} to use
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

//        auth.inMemoryAuthentication()
//                .withUser("张三")//账号
//                .password("{noop}123456") //密码 如果不配置密码编码器 会自动对密码进行加密  {noop} 明确告诉 security不加密
//                .roles("STUDENT","TEACHER")  //配置角色
//                .and()
//                .withUser("tom")
//                .password("{noop}123456")
//                .roles("STUDENT","ADMIN");  //配置角色
        //配置用户信息来自数据库 并且 指定密码编码器 目的是为了告诉springsecurity 是否要对对用户提交的密码进行加密然后比较
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());

    }

    //密码编码器
    //有二种常见的 ： 一种不加密直接比较密码 另一种通过hash加密后比较密码
    /*@Bean
    public PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }*/

    //配置角色权限信息
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                /*.antMatchers("/test/**").hasRole("ADMIN")*/ //test/del需要ADMIN角色才能访问
                .antMatchers("/user/regist").anonymous() //注册url可以匿名访问 不需要登录
                .antMatchers("/auth/login").anonymous() //注册url可以匿名访问 不需要登录
                .anyRequest().authenticated()  //其它的url必须登录之后才能访问 任何角色用户都可以访问 如果需要支持 就需要调用方法
                .and()
                .exceptionHandling()
                .accessDeniedHandler(new MyExceptionHandler()) //将自定义的异常处理解析器定义到security中
                .and()
                .formLogin(). //重写了当前类,security就不在支持form了
                and()
                .csrf().disable()          //在security中 第三方发请求就要关闭 csrf防御功能 前后端不分离 关闭后危险 前后端分离 关闭不影响 应该它不是通过session校验 而是token
                .cors()//开启springsecurity跨域
                .and()
                //自定义的过滤器要在UserNamePasswordAuthenticationFilter 之前执行 目的是为了手动生成Authentication对象
                //放到上下文中，就是为了让自定义的过滤器要在UserNamePasswordAuthenticationFilter得到用户信息，就认为登录了
                .addFilterBefore(jwtOnce, UsernamePasswordAuthenticationFilter.class);


    }
    @Resource
    private JwtOnce jwtOnce;
    //采用加密方式
    @Bean
    public PasswordEncoder passwordEncoder(){
        //采用加密方法 当登录时 会进行加密方法对密码进行加密 然后比较
        return new BCryptPasswordEncoder();
    }

    //认证管理器
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }



}
